mod proto;

use tonic::transport::{Endpoint, Channel};
use proto::hello::{say_client::SayClient,HelloRequest};
use tonic::{Request, Status};
use tonic::metadata::AsciiMetadataValue;
use std::iter::FromIterator;
use serde_json::{Map, Value, Number};
use std::collections::BTreeMap;
use prost_types::Struct;

#[tokio::main]
async fn main()-> Result<(), Box<dyn std::error::Error>> {
    let channel = new_channel("http://127.0.0.1:666", 10, 3)?;
    let mut client = SayClient::with_interceptor(channel, add_request_id(add_token));

    test_hello(&mut client,"hao are you").await;

    Ok(())
}

async fn test_hello<S:ToString+Send>(client:&mut SayClient<Channel>,msg:S){
    let value = msg.to_string();
    let mut request = HelloRequest{value, content:None };
    let ctx = serde_json::json!({
        "name":"wd",
        "age":12
    });
    if let serde_json::Value::Object(map) = ctx {
        wd_log::log_debug_ln!("加载自定义map信息");
        request.content = Some(Struct{fields:new_ctx_struct(map)});
    }
    let request = Request::new(request);
    match client.say_hello(request).await {
        Ok(o)=>{
            wd_log::log_info_ln!("server say:{}",o.get_ref().value);
        }
        Err(e)=>{
            wd_log::log_error_ln!("test_hello error:{}",e.to_string())
        }
    };
}

fn new_ctx_struct(map:Map<String,serde_json::Value>) -> BTreeMap<String, prost_types::Value> {
    let mut ctx:prost::alloc::collections::BTreeMap<String,prost_types::Value> = prost::alloc::collections::BTreeMap::new();
    for (key,value) in map.into_iter() {
        match value {
            serde_json::Value::String(s)=>{
                ctx.insert(key,prost_types::Value{kind:Some(prost_types::value::Kind::StringValue(s))});
            }
            serde_json::Value::Number(n)=>{
                if let Some(i) = n.as_f64() {
                    ctx.insert(key, prost_types::Value { kind: Some(prost_types::value::Kind::NumberValue(i)) });
                }
            }
            _ => {}
        };
        }
    return ctx;
}

//设置连接参数
fn new_channel(addr:&'static str,limit:usize,time_out:u64)->Result<Channel, Box<dyn std::error::Error>> {
    let ep = Endpoint::new(addr).unwrap()
        .concurrency_limit(limit)
        .timeout(std::time::Duration::from_secs(time_out))
        .connect_lazy()?;
    Ok(ep)
}

//添加token拦截器
fn  add_token(mut reqs:Request<()>) -> Result<Request<()>, Status>{
    let token = AsciiMetadataValue::from_str("fTSCcnnfIAUiEaNVmgXro7bwKtz2DXsUwi6Dcvt74qc=").unwrap();
    reqs.metadata_mut().insert("authorization",token);
    Ok(reqs)
}

//添加request_id拦截器
fn add_request_id<F>(f: F) -> impl Fn(Request<()>) -> Result<Request<()>, Status> + Send + Sync + 'static
    where F: Fn(Request<()>) -> Result<Request<()>, Status> + Send + Sync + 'static
{
    return move |mut reqs:Request<()>|{
        let token = AsciiMetadataValue::from_str("2021061201001").unwrap();
        reqs.metadata_mut().insert("request_id",token);
        f(reqs)
    }
}